package com.cv.platform.service.video;

import cn.hutool.core.util.ObjUtil;
import com.cv.framework.common.utils.json.JsonUtil;
import com.cv.framework.common.utils.object.BeanUtils;
import com.cv.framework.mybatis.core.query.QueryWrapperX;
import com.cv.framework.security.core.utils.SecurityFrameworkUtil;
import com.cv.platform.controller.platform.video.vo.dislike.CvVideoDislikeSaveReqVO;
import com.cv.platform.dal.dataobject.video.CvVideoDislikeDO;
import com.cv.platform.dal.mapper.video.CvVideoDislikeMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Map;

import static com.cv.platform.dal.redis.RedisKeyConstants.VIDEO_DISLIKE;
import static com.cv.platform.dal.redis.RedisKeyConstants.VIDEO_DISLIKE_DETAIL;

/**
 * 视频踩业务层接口实现类
 *
 * @author Charles_XDXD
 */
@Service
@Slf4j
public class CvVideoDislikeServiceImpl implements CvVideoDislikeService {

    @Resource
    private CvVideoDislikeMapper cvVideoDislikeMapper;

    @Resource
    private RedisTemplate redisTemplate;

    @Override
    public Boolean getVideoDislikeStatus(Long videoId) {
        HashOperations<String, String, String> ops = redisTemplate.opsForHash();
        Long userId = SecurityFrameworkUtil.getLoginUserId();
        String key = String.format(VIDEO_DISLIKE_DETAIL, userId, videoId);

        String cache = ops.get(VIDEO_DISLIKE, key);
        CvVideoDislikeSaveReqVO saveReqVO = JsonUtil.parseObject(cache, CvVideoDislikeSaveReqVO.class);
        if (ObjUtil.isNotEmpty(saveReqVO)) {
            return saveReqVO.getIsDislike();
        }

        QueryWrapperX<CvVideoDislikeDO> wrapper = new QueryWrapperX<CvVideoDislikeDO>()
                .eq("video_id", videoId)
                .eq("user_id", userId);
        CvVideoDislikeDO cvVideoDislikeDO = cvVideoDislikeMapper.selectOne(wrapper);

        if (ObjUtil.isNotEmpty(cvVideoDislikeDO)) {
            saveReqVO = BeanUtils.toBean(cvVideoDislikeDO, CvVideoDislikeSaveReqVO.class);
            saveReqVO.setIsDislike(Boolean.TRUE);
            saveReqVO.setNeedUpdate(Boolean.FALSE);
            ops.put(VIDEO_DISLIKE, key, JsonUtil.toJsonString(saveReqVO));
            return true;
        } else {
            saveReqVO = CvVideoDislikeSaveReqVO.builder()
                    .videoId(videoId)
                    .userId(userId)
                    .isDislike(Boolean.FALSE)
                    .needUpdate(Boolean.FALSE)
                    .build();
            ops.put(VIDEO_DISLIKE, key, JsonUtil.toJsonString(saveReqVO));
            return false;
        }
    }

    @Override
    public Integer getVideoDislikeCount(Long videoId) {
        QueryWrapperX<CvVideoDislikeDO> wrapper = new QueryWrapperX<CvVideoDislikeDO>()
                .eq("video_id", videoId);
        Integer count = Math.toIntExact(cvVideoDislikeMapper.selectCount(wrapper));

        // 这里只更新当前用户的操作，其它用户不更新。因为数据量一旦大，会影响性能。因此可以牺牲掉这一部分，不需要保证实时性。
        HashOperations<String, String, String> ops = redisTemplate.opsForHash();
        Long userId = SecurityFrameworkUtil.getLoginUserId();
        String key = String.format(VIDEO_DISLIKE_DETAIL, userId, videoId);
        String cache = ops.get(VIDEO_DISLIKE, key);
        CvVideoDislikeSaveReqVO saveReqVO = JsonUtil.parseObject(cache, CvVideoDislikeSaveReqVO.class);
        if (ObjUtil.isNotEmpty(saveReqVO) && Boolean.TRUE.equals(saveReqVO.getNeedUpdate())) {
            if (Boolean.TRUE.equals(saveReqVO.getIsDislike())) {
                count++;
            } else if (ObjUtil.isNotEmpty(saveReqVO.getId())) {
                count--;
            }
        }

        return count;
    }

    @Override
    public void saveVideoDislike(Long videoId) {
        HashOperations<String, String, String> ops = redisTemplate.opsForHash();
        Long userId = SecurityFrameworkUtil.getLoginUserId();
        String key = String.format(VIDEO_DISLIKE_DETAIL, userId, videoId);

        String cache = ops.get(VIDEO_DISLIKE, key);
        CvVideoDislikeSaveReqVO saveReqVO = JsonUtil.parseObject(cache, CvVideoDislikeSaveReqVO.class);
        if (ObjUtil.isNotEmpty(saveReqVO)) {
            saveReqVO.setIsDislike(Boolean.TRUE);
            saveReqVO.setNeedUpdate(Boolean.TRUE);
        } else {
            saveReqVO = CvVideoDislikeSaveReqVO.builder()
                    .videoId(videoId)
                    .userId(userId)
                    .isDislike(Boolean.TRUE)
                    .needUpdate(Boolean.TRUE)
                    .build();
        }
        ops.put(VIDEO_DISLIKE, key, JsonUtil.toJsonString(saveReqVO));
    }

    @Override
    public void cancelVideoDislike(Long videoId) {
        HashOperations<String, String, String> ops = redisTemplate.opsForHash();
        Long userId = SecurityFrameworkUtil.getLoginUserId();
        String key = String.format(VIDEO_DISLIKE_DETAIL, userId, videoId);

        String cache = ops.get(VIDEO_DISLIKE, key);
        CvVideoDislikeSaveReqVO saveReqVO = JsonUtil.parseObject(cache, CvVideoDislikeSaveReqVO.class);
        if (ObjUtil.isNotEmpty(saveReqVO)) {
            saveReqVO.setIsDislike(Boolean.FALSE);
            saveReqVO.setNeedUpdate(Boolean.TRUE);
        } else {
            saveReqVO = CvVideoDislikeSaveReqVO.builder()
                    .videoId(videoId)
                    .userId(userId)
                    .isDislike(Boolean.FALSE)
                    .needUpdate(Boolean.TRUE)
                    .build();
        }
        ops.put(VIDEO_DISLIKE, key, JsonUtil.toJsonString(saveReqVO));
    }

    @Override
    public void syncDislikeData() {
        log.info("[CvVideoDislikeServiceImpl.syncDislikeData] 开始同步踩数据");
        HashOperations<String, String, String> ops = redisTemplate.opsForHash();

        ScanOptions scanOptions = ScanOptions.scanOptions().build();
        try (Cursor<Map.Entry<String, String>> cursor = ops.scan(VIDEO_DISLIKE, scanOptions)) {
            while (cursor.hasNext()) {
                Map.Entry<String, String> entry = cursor.next();
                String key = entry.getKey();
                String cache = entry.getValue();
                CvVideoDislikeSaveReqVO saveReqVO = JsonUtil.parseObject(cache, CvVideoDislikeSaveReqVO.class);

                if (ObjUtil.isNotEmpty(saveReqVO) && Boolean.TRUE.equals(saveReqVO.getNeedUpdate())) {
                    CvVideoDislikeDO recordDO = BeanUtils.toBean(saveReqVO, CvVideoDislikeDO.class);
                    if (saveReqVO.getIsDislike().equals(Boolean.TRUE)) {
                        cvVideoDislikeMapper.insertOrUpdate(recordDO);
                    } else {
                        if (ObjUtil.isNotEmpty(recordDO.getId())) {
                            cvVideoDislikeMapper.deleteById(recordDO.getId());
                        }
                    }

                    if (ObjUtil.isNotEmpty(recordDO.getId())) {
                        saveReqVO.setId(recordDO.getId());
                    }
                    saveReqVO.setNeedUpdate(Boolean.FALSE);
                    ops.put(VIDEO_DISLIKE, key, JsonUtil.toJsonString(saveReqVO));
                }
            }
        } catch (Exception e) {
            log.error("[CvVideoLikeServiceImpl.syncLikeData] 同步踩数据异常：", e);
        } finally {
            log.info("[CvVideoLikeServiceImpl.syncLikeData] 结束同步踩数据");
        }
    }
}
